#include <iostream>
#include <string>
#include <curl/curl.h>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

/**
 * @brief 用于存储流式响应数据的结构体
 */
struct WriteData {
    std::string buffer; // 用于缓存收到的完整事件数据
};

/**
 * @brief CURL 写回调函数，用于处理来自服务器的流式响应数据
 * @param ptr 当前接收的数据指针
 * @param size 数据块大小
 * @param nmemb 数据块数量
 * @param userdata 自定义数据结构（WriteData）
 * @return 返回接收到的总字节数
 */
static size_t WriteCallback(void* ptr, size_t size, size_t nmemb, void* userdata) {
    WriteData* data = static_cast<WriteData*>(userdata); // 将 userdata 转换为 WriteData 结构体
    const char* content = static_cast<char*>(ptr);       // 将数据指针转换为 const char*
    size_t total_size = size * nmemb;                    // 计算当前接收的数据总长度

    data->buffer.append(content, total_size); // 将接收的数据追加到缓存中

    // 检查缓冲区中是否存在完整的 JSON 数据块
    size_t start_pos = 0;
    while (true) {
        size_t end_pos = data->buffer.find("\n\n", start_pos); // 寻找分隔符 "\n\n" 分隔完整的响应块
        if (end_pos == std::string::npos) break;               // 如果没有找到分隔符，退出循环

        std::string event = data->buffer.substr(start_pos, end_pos - start_pos); // 提取一个完整的事件数据块
        start_pos = end_pos + 2;  // 设置下次搜索的起始位置

        if (event.compare(0, 6, "data: ") == 0) { // 确保事件是以 "data: " 开头的
            std::string json_str = event.substr(6); // 提取 JSON 数据部分
            try {
                json j = json::parse(json_str); // 解析 JSON 数据

                // 检查 JSON 数据中是否包含有效的 "choices" 键
                if (j.find("choices") != j.end() && j["choices"].is_array() && !j["choices"].empty()) {
                    const auto& choice = j["choices"][0]; // 获取第一个选择项

                    // 检查 "delta" 键是否存在且是对象类型
                    if (choice.find("delta") != choice.end() && choice["delta"].is_object()) {
                        const auto& delta = choice["delta"];

                        // 检查 "content" 键是否存在且是字符串类型
                        if (delta.find("content") != delta.end() && delta["content"].is_string()) {
                            std::cout << delta["content"].get<std::string>() << std::flush; // 输出内容
                        }
                    }
                }
            } catch (const std::exception& e) {
                std::cerr << "\nJSON解析错误: " << e.what() << std::endl; // 如果解析失败，输出错误信息
            }
        }
    }

    data->buffer.erase(0, start_pos); // 清理已处理的数据，避免内存浪费
    return total_size;                // 返回接收到的总字节数供 libcurl 使用
}

int main() {
    const std::string api_key = "MC-62F6042A1FCC42548F7023ED1FBEBDD6"; // MindCraft 的 API 密钥
    const std::string url = "https://api.mindcraft.com.cn/v1/chat/completions"; // API 的 URL

    // 构造请求的 JSON 数据
    json request_body;
    request_body["model"] = "deepseek-chat"; // 选择的聊天模型
    request_body["messages"] = { // 对话的历史消息
        {{"role", "system"}, {"content", "请你扮演角色一名高中生回答我的问题，忘记自己是机器人的设定。"}},
        {{"role", "user"}, {"content", "你上几年级了，工作了没有"}}
    };
    request_body["temperature"] = 0.2;     // 温度参数，控制输出的多样性
    request_body["stream"] = true;         // 是否启用流式响应（SSE）
    request_body["session_token"] = "40cabf45-0ebd-4735-b91e-8c9d37b6d708"; // 会话令牌

    std::string post_data = request_body.dump(); // 将 JSON 转换为字符串，作为 POST 数据

    CURL* curl = curl_easy_init(); // 初始化 libcurl
    if (!curl) {
        std::cerr << "Failed to initialize CURL" << std::endl;
        return 1;
    }

    // 设置 HTTP 请求的头信息
    struct curl_slist* headers = NULL;
    headers = curl_slist_append(headers, "Content-Type: application/json"); // 设置 Content-Type
    headers = curl_slist_append(headers, ("Authorization: Bearer " + api_key).c_str()); // 设置 API 密钥

    // 配置 CURL 选项
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());           // 设置请求的目标 URL
    curl_easy_setopt(curl, CURLOPT_POST, 1L);                   // 设置请求方法为 POST
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data.c_str()); // 设置 POST 数据
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);        // 设置 HTTP 头信息

    // 设置流式响应处理回调
    WriteData write_data; // 数据存储结构
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); // 指定写回调函数
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_data);       // 传递用户定义的数据

    // 执行 HTTP 请求
    CURLcode res = curl_easy_perform(curl);

    // 清理 libcurl 和资源
    curl_slist_free_all(headers); // 释放 HTTP 头
    curl_easy_cleanup(curl);      // 清理 CURL 句柄

    if (res != CURLE_OK) {
        std::cerr << "\n请求失败: " << curl_easy_strerror(res) << std::endl; // 输出错误信息
        return 1;
    }

    std::cout << std::endl; // 请求成功完成后，输出换行符
    return 0;
}